﻿using System;
using System.Text;
using System.Collections.Generic;

namespace Ooaavee.Scripting
{
    internal sealed class Code
    {
        /// <summary>
        /// No public constructor available, use GetCode method.
        /// </summary>
        private Code() { }

        /// <summary>
        /// The namespace.
        /// </summary>
        internal string Namespace { set; get; }

        /// <summary>
        /// The class name.
        /// </summary>
        internal string Class { set; get; }

        /// <summary>
        /// The full source code.
        /// </summary>
        internal string FullSourceCode { set; get; }

        /// <summary>
        /// The method name.
        /// </summary>
        internal string Method { set; get; }

        internal static Code GetCode(string lines, string baseClass, References references, Language language)
        {
            Template template = Template.GetTemplate(language);

            Code code = new Code();
            code.Namespace = NewMemberName();            
            code.Class = NewMemberName();            
            code.Method = NewMemberName();

            string s = template.FullTemplateText;
            s = s.Replace("$NAMESPACE$", code.Namespace);
            s = s.Replace("$CLASS$", code.Class);
            s = s.Replace("$BASECLASS$", baseClass);            
            s = s.Replace("$METHOD$", code.Method);
            s = s.Replace("$PARAMETERS$", NewMemberName());            
            s = s.Replace("$CODE$", lines);

            StringBuilder namespaces = new StringBuilder();
            foreach (string ns in GetNamespaces(references))
            {
                namespaces.AppendLine(FormatNamespaceToCode(ns, language));
            }
            s = s.Replace("$NAMESPACES$", namespaces.ToString());

            code.FullSourceCode = s;

            return code;
        }

        /// <summary>
        /// Returns a new string that can be used as a member name in code.
        /// </summary>
        /// <returns>a new member name</returns>
        private static string NewMemberName()
        {
            return string.Format("_{0}", Guid.NewGuid().ToString().Replace('-', '_'));
        }

        private static string FormatNamespaceToCode(string ns, Language language)
        {
            switch (language)
            {
                case Language.CSharp:
                    return string.Format("using {0};", ns);
                case Language.VisualBasic:
                    return string.Format("Imports {0}", ns);
                default:
                    throw new NotImplementedException();
            }
        }

        private static IEnumerable<string> GetNamespaces(IEnumerable<Reference> references)
        {
            List<string> namespaces = new List<string>();

            foreach (Reference reference in references)
            {
                foreach (string ns in reference.Namespaces)
                {
                    if (namespaces.Contains(ns) == false)
                    {
                        namespaces.Add(ns);
                    }
                }                
            }

            namespaces.Sort((ns1, ns2) => ns1.CompareTo(ns2));

            return namespaces;
        }

    }
}
